package spring.bean;

import cn.hutool.core.lang.Filter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.stereotype.Component;
import reflection.ScanClassUtils;
import spring.bean.annotations.BeanRegistryAfterOper;

import java.util.Set;

/**
 * spring 的 bean 在注册完后 会调用实现了 {@link BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry} 方法
 */
@Slf4j
@Component
public class SpringBeanRegistryAfterOperParse implements BeanDefinitionRegistryPostProcessor {

    /**
     * bean 名称生成方式
     */
    private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

    //指定将要替换或者注册的新的 bean 所在的 package
    public final static String[] SCAN_PACKAGES = new String[]{};

    @SneakyThrows
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        if(SCAN_PACKAGES != null && SCAN_PACKAGES.length > 0){
            Set<Class<?>> set = ScanClassUtils.getClasses(SCAN_PACKAGES);
            for (Class<?> clazz : set) {
                if(clazz.isAnnotationPresent(BeanRegistryAfterOper.class)){
                    BeanRegistryAfterOper plus = clazz.getAnnotation(BeanRegistryAfterOper.class);
                    String[] beanNames = plus.value();
                    boolean registerBean = plus.registerBean();
                    beanNames = ArrayUtil.filter(beanNames, (Filter<String>) s -> StrUtil.isNotBlank(s));
                    if(ArrayUtil.isEmpty(beanNames)){
                        beanNames = new String[1];
                    }
                    BeanDefinition beanDefinition = null;
                    if(beanNames.length <= 1){
                        beanDefinition = new RootBeanDefinition(clazz);
                        if(StrUtil.isBlank(beanNames[0])){
                            beanNames[0] = this.beanNameGenerator.generateBeanName(beanDefinition, registry);
                        }
                    } else if(beanNames.length > 1){
                        beanDefinition = new ScannedGenericBeanDefinition(new CachingMetadataReaderFactory().getMetadataReader(clazz.getName()));
                    }
                    for (String beanName : beanNames) {
                        if(registry.containsBeanDefinition(beanName)){
                            registry.removeBeanDefinition(beanName);
                        }
                    }
                    if(beanDefinition != null){
                        beanDefinition.setAutowireCandidate(true);
                        if(registerBean){
                            registry.registerBeanDefinition(beanNames[0], beanDefinition);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}
